package com.nuumoblie.bluetoothtools.bt;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.Environment;
import android.os.SystemClock;
import android.util.Log;

import com.nuumoblie.bluetoothtools.AppApplication;
import com.nuumoblie.bluetoothtools.utils.MD5;
import com.nuumoblie.bluetoothtools.utils.MainThreadExecutor;
import com.nuumoblie.bluetoothtools.utils.Utils;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.ExecutorService;

import es.dmoral.toasty.Toasty;

/**
 * 客户端和服务端的基类，用于管理socket长连接
 */
public class BtOptBase {
    static final UUID SPP_UUID = UUID.fromString("11111101-0000-1000-8000-00805F9B34FC");
    static final UUID SPP_UUID_SECURE = UUID.fromString("11111102-0000-1000-8000-00805F9B34FC");
    private static final String FILE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/bluetooth/";
    private static final int FLAG_MSG = 0;  //消息标记
    private static final int FLAG_FILE = 1; //文件标记
    private static final int FLAG_FILE_VERIFY = 2;//文件校验

    private BluetoothSocket mSocket;
    private DataOutputStream mOut;
    private Listener mListener;
    private boolean isRead;
    private boolean isSendingFile;
    private boolean isSending;
    public static boolean isConnecting = false;

//    private final MainThreadExecutor mUiExecutor = new MainThreadExecutor();

    BtOptBase(Listener listener) {
        mListener = listener;
    }

    /**
     * 循环读取对方数据(若没有数据，则阻塞等待)
     */
    void loopRead(BluetoothSocket socket) {
        mSocket = socket;
        try {
            if (!mSocket.isConnected())
                mSocket.connect();
            notifyUI(Listener.CONNECTED, mSocket.getRemoteDevice());
            mOut = new DataOutputStream(mSocket.getOutputStream());
            DataInputStream in = new DataInputStream(mSocket.getInputStream());
            isRead = true;
            isConnecting = false;
            while (isRead) { //死循环读取
                switch (in.readInt()) {
                    case FLAG_MSG: //读取短消息
                        String msg = in.readUTF();
                        notifyUI(Listener.MSG, "接收短消息：" + msg);
                        break;
                    case FLAG_FILE: //读取文件
//                        Utils.mkdirs(FILE_PATH);
//                        String fileName = in.readUTF(); //文件名
//                        long fileLen = in.readLong(); //文件长度
//                        // 读取文件内容
//                        long len = 0;
//                        int r;
//                        byte[] b = new byte[4 * 1024];
//                        FileOutputStream out = new FileOutputStream(FILE_PATH + fileName);
//                        notifyUI(Listener.MSG, "正在接收文件(" + fileName + "),请稍后...");
//                        while ((r = in.read(b)) != -1) {
//                            out.write(b, 0, r);
//                            len += r;
//                            if (len >= fileLen)
//                                break;
//                        }
//                        notifyUI(Listener.MSG, "文件接收完成(存放在:" + FILE_PATH + ")");
                        readFile(in, false);
                        break;
                    case FLAG_FILE_VERIFY:
                        readFile(in, true);

                        break;
                }
            }
        } catch (Throwable e) {
            e.printStackTrace();
            isConnecting = false;
            close();
        }
    }

    private void readFile(DataInputStream in, boolean isNeedVerify) throws IOException {
        Utils.mkdirs(FILE_PATH);
        String fileName = in.readUTF(); //文件名
        String md5 = "";
        long fileLen = in.readLong(); //文件长度
        if (isNeedVerify) {
            md5 = in.readUTF();
            fileName = fileName+"-"+System.currentTimeMillis();
        }

        notifyUI(Listener.MSG, "file info:filename-" + fileName + "  file length-" + fileLen + "  md5-" + md5);
        // 读取文件内容
        long len = 0;
        int r;
        byte[] b = new byte[4 * 1024];
        FileOutputStream out = new FileOutputStream(FILE_PATH + fileName);
        notifyUI(Listener.MSG, "正在接收文件(" + fileName + "),请稍后...");
        long startTime = SystemClock.elapsedRealtime();
        long persLen = 0;
        while ((r = in.read(b)) != -1) {
//            Log.v("lmjssjj","recevier len:"+r);
            out.write(b, 0, r);
            len += r;
            if ((SystemClock.elapsedRealtime() - startTime) >= 1000) {
                startTime = SystemClock.elapsedRealtime();
                notifyUI(Listener.SPEED, Utils.getFileSizeDescription(len - persLen) + "/s" + "--" + Utils.getFileSizeDescription(len) + "--" + Utils.getFileSizeDescription(fileLen));
                persLen = len;
            }
//            Log.v("lmjssjj",""+len);
            if (len >= fileLen) {
                notifyUI(Listener.SPEED, "传输完成");
                break;
            }
        }
        out.close();
        notifyUI(Listener.MSG, "文件接收完成(存放在:" + FILE_PATH + ")");
        if (isNeedVerify) {
            notifyUI(Listener.MSG, "正在校验...");
            String localMd5 = MD5.getFileMD5(new File(FILE_PATH + fileName));
            notifyUI(Listener.MSG, "recevier- file md5" + localMd5);
            if (localMd5.equals(md5)) {
                notifyUI(Listener.MSG, "校验成功");
            } else {
                notifyUI(Listener.MSG, "校验失败");
            }
        }
    }

    /**
     * 发送短消息
     */
    public void sendMsg(String msg) {
        if (checkSend()) return;
        isSending = true;
        try {
            mOut.writeInt(FLAG_MSG); //消息标记
            mOut.writeUTF(msg);
            mOut.flush();
            notifyUI(Listener.MSG, "发送短消息：" + msg);
        } catch (Throwable e) {
            e.printStackTrace();
            close();
        }
        isSending = false;
    }

    /**
     * 发送文件
     */
    public void sendFile(final String filePath, final boolean isVerify) {
        if (checkSend()) return;
        isSending = true;
        Utils.EXECUTOR.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    FileInputStream in = new FileInputStream(filePath);
                    File file = new File(filePath);
                    String md5 = "";
                    if (isVerify) {
                        mOut.writeInt(FLAG_FILE_VERIFY); //文件标记
                    } else {
                        mOut.writeInt(FLAG_FILE); //文件标记
                    }
                    mOut.writeUTF(file.getName()); //文件名
                    mOut.writeLong(file.length()); //文件长度

                    notifyUI(Listener.MSG, "发送文件name-" + file.getName() + "  length-" + file.length());

                    if (isVerify) {
                        notifyUI(Listener.MSG, "正在计算文件MD5...");
                        md5 = MD5.getFileMD5(new File(filePath));
                        mOut.writeUTF(md5);
                    }

                    int r;
                    byte[] b = new byte[4 * 1024];
                    notifyUI(Listener.MSG, "正在发送文件(" + filePath + "),请稍后...");
                    long filelen = file.length();
                    long alreadySend = 0;
                    long pers = 0;
                    long startTime = SystemClock.elapsedRealtime();
                    isSendingFile = true;
                    while ((r = in.read(b)) != -1&&isSendingFile) {
                        mOut.write(b, 0, r);
                        alreadySend += r;
                        if (SystemClock.elapsedRealtime() - startTime >= 1000) {
                            startTime = SystemClock.elapsedRealtime();
                            notifyUI(Listener.SPEED, Utils.getFileSizeDescription(alreadySend - pers) + "/s" + "--" + Utils.getFileSizeDescription(alreadySend) + "--" + Utils.getFileSizeDescription(filelen));
                            pers = alreadySend;
                        }
//                        Log.v("lmjssjj",alreadySend+"");
                    }
                    mOut.flush();
                    in.close();
                    in = null;
                    if (isSendingFile) {
                        notifyUI(Listener.SPEED, "传输完成");
                        notifyUI(Listener.MSG, "文件发送完成." + md5);
                    }else {
                        notifyUI(Listener.MSG, "断开传输...");
                    }
                    isSendingFile = false;
                } catch (Throwable e) {
                    e.printStackTrace();
                    close();
                }
                isSending = false;
            }
        });
    }

    /**
     * 释放监听引用(例如释放对Activity引用，避免内存泄漏)
     */
    public void unListener() {
        mListener = null;
    }

    public void disConnect() {
        try {
            isRead = false;
            isConnecting = false;
            if (mOut!=null){
                mOut.close();
                mOut = null;
            }
            if (mSocket != null) {
                mSocket.close();
                mSocket = null;
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    /**
     * 关闭Socket连接
     */
    public void close() {
        try {
            isSendingFile = false;
            isRead = false;
            if (mOut!=null){
                mOut.close();
                mOut = null;
            }
            if (mSocket != null) {
                mSocket.close();
                mSocket = null;
            }
            notifyUI(Listener.DISCONNECTED, null);
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    /**
     * 当前设备与指定设备是否连接
     */
    public boolean isConnected(BluetoothDevice dev) {
        boolean connected = (mSocket != null && mSocket.isConnected());
        if (dev == null)
            return connected;
        return connected && mSocket.getRemoteDevice().equals(dev);
    }

    // ============================================通知UI===========================================================
    private boolean checkSend() {
        if (isSending) {
            Toasty.warning(AppApplication.getInstance(), "正在发送其它数据,请稍后再发...", 0).show();
            return true;
        }
        return false;
    }

    protected void notifyUI(final int state, final Object obj) {
//        APP.runUi();
        AppApplication.runOnMainUI(new Runnable() {
            @Override
            public void run() {
                try {
                    if (mListener != null)
                        mListener.socketNotify(state, obj);
                } catch (Throwable e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public interface Listener {
        int DISCONNECTED = 0;
        int CONNECTED = 1;
        int MSG = 2;
        int SPEED = 3;

        void socketNotify(int state, Object obj);
    }
}
